package com.utils.commons;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

/**
 * @author geliyang
 *
 */
public class CopyProperties extends BeanUtils {

	/**
	 * 目标属性不为null则不复制
	 * 
	 * @param source
	 * @param target
	 * @throws BeansException
	 */
	public static void copyToNull(Object source, Object target, @Nullable String... ignoreProperties) throws BeansException {

		Assert.notNull(source, "Source must not be null");
		Assert.notNull(target, "Target must not be null");

		Class<?> actualEditable = target.getClass();
		PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
		
		List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);

		for (PropertyDescriptor targetPd : targetPds) {
			Method tReadMethod = targetPd.getReadMethod();
			try {
				if (tReadMethod.invoke(target) == null) {// 判断目标属性是否为空
					Method writeMethod = targetPd.getWriteMethod();
					if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
						PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
						if (sourcePd != null) {
							Method readMethod = sourcePd.getReadMethod();
							if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0],
									readMethod.getReturnType())) {
								if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
									readMethod.setAccessible(true);
								}
								Object value = readMethod.invoke(source);
								if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
									writeMethod.setAccessible(true);
								}
								writeMethod.invoke(target, value);
							}
						}
					}
				}
			} catch (Throwable ex) {
				throw new FatalBeanException(
						"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
			}
		}
	}

	/**
	 * 源属性为空则不复制
	 * 
	 * @param source
	 * @param target
	 * @throws BeansException
	 */
	public static void nullNotCopy(Object source, Object target, @Nullable String... ignoreProperties) throws BeansException {

		Assert.notNull(source, "Source must not be null");
		Assert.notNull(target, "Target must not be null");

		Class<?> actualEditable = source.getClass();
		PropertyDescriptor[] sourcePds = getPropertyDescriptors(actualEditable);
		
		List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);

		for (PropertyDescriptor sourcePd : sourcePds) {
			Method sReadMethod = sourcePd.getReadMethod();
			try {
				if (sReadMethod.invoke(source) != null) {
					PropertyDescriptor targetPd = getPropertyDescriptor(target.getClass(), sourcePd.getName());
					if (targetPd != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
						Method writeMethod = targetPd.getWriteMethod();
						if (writeMethod != null) {
							if (sReadMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0],
									sReadMethod.getReturnType())) {
								if (!Modifier.isPublic(sReadMethod.getDeclaringClass().getModifiers())) {
									sReadMethod.setAccessible(true);
								}
								Object value = sReadMethod.invoke(source);
								if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
									writeMethod.setAccessible(true);
								}
								writeMethod.invoke(target, value);
							}
						}
					}
				}
			} catch (Throwable ex) {
				throw new FatalBeanException(
						"Could not copy property '" + sourcePd.getName() + "' from source to target", ex);
			}
		}
	}
}
